#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include "Socket.hpp"

using namespace socket_ns;

class EpollServer
{
    const static int num = 128;
public:
    EpollServer(uint32_t port) : _port(port), _listensock(std::make_unique<TcpSoket>())
    {
        _listensock->BuildListenSocket(port); // 创建 listen 套接字
        _epfd = epoll_create(1); // 创建 epoll 模型
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create error\n");
            exit(1);
        }
        LOG(INFO, "epoll create success, epfd:%d\n", _epfd);
    }

    void Init()
    {
        // 将 listensock 添加到 epoll 红黑树中
        struct epoll_event ev;
        ev.data.fd = _listensock->Sockfd();
        ev.events = EPOLLIN | EPOLLET;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Sockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error\n");
        }
        LOG(INFO, "epoll_ctl succuss, add a new sockfd: %d\n", _listensock->Sockfd());
    }

    std::string EventToString(uint32_t event)
    {
        std::string ret;
        if (event & EPOLLIN)
            ret += "EPOLLIN";
        if (event & EPOLLOUT)
            ret += "EPOLLOUT";
        return ret;
    }

    void Accepter()
    {
        // 此时新连接到来，一定可以调用 accept
        InetAddr client;
        int fd = _listensock->Acceptor(&client);

        if (fd < 0)
        {
            LOG(FATAL, "accept error\n");
            return;
        }

        LOG(INFO, "接收到一个新连接: %s\n", client.AddrStr().c_str());
        // 将新连接添加到fd
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error\n");
            return;
        }
        LOG(INFO, "epoll_ctl success, fd: %d\n", fd);
    }

    void HandlerIO(int fd)
    {
        char buffer[4096]; // 缓冲区

        int n = recv(fd, buffer, sizeof(buffer)-1, 0); // 一定不会阻塞
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "recv #" << buffer << std::endl;
            // 构建回显信息，一个简单的 http 响应
            std::string echo_info = "HTTP/1.0 200 OK\r\n";
            std::string content = "<html><body><h1>hello world!</h1></body></html>";

            echo_info += "Content_Type: text/html\r\n";
            echo_info += "Content_Length: " + std::to_string(content.size()) + "\r\n";
            echo_info += "\r\n";
            echo_info += content;

            ::send(fd, echo_info.c_str(), echo_info.size(), 0);
        }
    }

    void HandlerEvent(int n)
    {
        for (int i = 0; i < n; i++)
        {
            struct epoll_event ev = _recv[i];
            LOG(INFO, "fd %d 上面有事件就绪，事件是 %s\n", ev.data.fd, EventToString(ev.events).c_str());

            //  读事件就绪，处理
            if (ev.events & EPOLLIN)
            {
                if (ev.data.fd == _listensock->Sockfd())
                    Accepter(); // listensockfd就绪，说明新连接到来
                else
                    HandlerIO(ev.data.fd); // 普通fd就绪，表明可以进行IO了
            }
        }
    }
    void Loop()
    {
        while(1)
        {
            int n = epoll_wait(_epfd, _recv, num, -1); // -1: 阻塞等待
            switch (n)
            {
            case 0:
                LOG(INFO, "epoll wait time out\n");
                break;
            case -1:
                LOG(FATAL, "epoll wait error\n");
                break;
            default:
                LOG(INFO, "epoll wait success\n");
                // HandlerEvent(n); // 处理就绪事件
                break;
            }
        }
    }
private:
    uint32_t _port;
    std::unique_ptr<Socket> _listensock;
    int _epfd; // epoll 模型文件描述符
    struct epoll_event _recv[num]; // 用户缓冲区，接收就绪事件
};